#!/usr/bin/env python
# -*- coding=UTF-8 -*-

import argparse
import pymysql
import os

def parse_params():
    '''
    解析命令行参数

    --host, -H 数据库IP地址
    --port, -P 数据库端口号
    --user, -u 数据库用户名
    --passwd, -p 数据库密码
    --baseline, -b 基线，小于该版本的SQL文件不会被应用
    --operation, -o 操作(migration/info/clean)
    --schema, -s 待操作的数据库（微服务）名
    '''
    parser = argparse.ArgumentParser(description='')
    parser.add_argument('--host', '-H', help='')
    parser.add_argument('--port', '-P', help='', default=3306, type=int)
    parser.add_argument('--user', '-u', help='')
    parser.add_argument('--passwd', '-p', help='')
    parser.add_argument('--baseline', '-b', help='', default=0)
    parser.add_argument('--operation', '-o', help='')
    parser.add_argument('--schema', '-s', help='')
    return parser.parse_args()

def do_info(conn, schema):
    '''
    查询当前版本执行信息

    Args:
        conn 数据库连接
        schema 要查询的版本信息
    '''
    cursor = conn.cursor()
    sql = 'select installed_rank, version, description, type, script, installed_on, table_schema from schema_history where table_schema = "%s"' % (schema)
    try:
        cursor.execute(sql)
        rows = cursor.fetchall()
        for row in rows:
            result = 'installed_rank: %s, version: %s, description: %s, type: %s, script: %s, installed_on: %s, table_schema: %s' % row 
            print(result)
    finally:
        cursor.close()
        conn.close()

def do_clean(conn, schema):
    '''
    清理版本元数据表中的信息，删除指定schema的

    Args:
        conn 数据库连接
        schema 要查询的版本信息 
    '''
    cursor = conn.cursor()
    sql = 'delete from schema_history where table_schema = "%s"' % (schema)
    try:
        
        cursor.execute(sql)
        conn.commit()
    except:
        conn.rollback()
    finally:
        cursor.close()
        conn.close()

def do_migration(conn, schema, baseline):
    '''
    数据库升级方法，依次执行编写好的脚本

    Args:
        conn 数据库连接
        schema 要查询的版本信息
        baseline 基线
    '''
    ## 返回已经排序的文件信息元组列表
    file_list = check_files()
    ## 获得当前版本
    current_version = get_current_version(conn, schema)
    ## 寻找比当前版本高的版本
    for file in file_list:
        if int(file[1]) <= current_version or int(file[1]) <= baseline: # 小于等于当前版本和小于等于基线的都删除
            file_list.remove(file)
    for file in file_list:
        if execute_sql_file(conn, file[5]):
            meta = (convert_version(int(file[1])), file[3], 'SQL', file[4], 1, schema)
            update_meta_table(conn, schema, meta)
        else:
            print('Run SQL Script fail!')
    conn.commit()

def convert_version(version):
    '''
        将数字形式的版本号转换为小数点分隔的版本号
        如400会转换为4.0.0

        Args:
            version 数字形式版本号
    '''
    x = version // 100
    y = version % 100 // 10
    z = version % 100 % 10
    return str(x) + '.' + str(y) + '.' + str(z)

def execute_sql_file(conn, sql_file):
    '''
        执行SQL文件

        Args:
            conn 数据库连接
            sql_file 要被执行的sql文件完整路径
    '''
    cursor = conn.cursor()
    with open(sql_file, 'r') as sql:
        sql_list = sql.read().split(';')[:-1]
        sql_list = [x.replace('\n', ' ') if '\n' in x else x for x in sql_list]
    try:
        for sql_item in sql_list:
            cursor.execute(sql_item)
        return True
    except:
        conn.rollback()
        return False
    finally:
        cursor.close()

def update_meta_table(conn, schema, meta):
    '''
        写入元数据表

        Args:
            conn 数据库连接
            schema 数据库名称
            meta 元数据信息，元组
    '''
    sql = 'insert into schema_history(version, description, type, script, success, table_schema) values (\'%s\', \'%s\', \'%s\', \'%s\', \'%s\', \'%s\')' % meta
    cursor = conn.cursor()
    try:
        cursor.execute(sql)
    finally:
        cursor.close()

def get_current_version(conn, schema):
    '''
        获得数据库中记录的当前schema的最新版本号

        Args:
            conn 数据库连接
            schema 数据库名称
    '''
    sql = 'select version from schema_history where table_schema = "%s" order by installed_rank desc limit 1' % (schema)
    cursor = conn.cursor()
    try:
        cursor.execute(sql)
        rows = cursor.fetchall()
        return int(rows[0][0].replace('.', ''))
    except:
        return 0
    finally:
        cursor.close()
    return 0

def check_files():
    '''
    检索sql目录，返回文件名列表
    '''
    file_list = []
    for root, _, files in os.walk(r'.\sql'):
        for file in files:
            file_meta = file.split('.')[0].split('__')
            file_meta.append(file)
            file_meta.append(os.path.join(root, file))
            file_list.append(tuple(file_meta))
    return sorted(file_list, key=lambda x: x[1], reverse=False)

def main():
    args = parse_params()
    settings = {'host': args.host, 
                'port': args.port, 
                'user': args.user, 
                'passwd': args.passwd, 
                'baseline': args.baseline, 
                'operation': args.operation, 
                'schema': args.schema}
    conn = pymysql.connect(host=settings['host'], 
                           port=settings['port'],
                           user=settings['user'],
                           password=settings['passwd'],
                           database=settings['schema'])
    conn.autocommit = False
    if settings['operation'] == 'info':
        do_info(conn, settings['schema'])
    elif settings['operation'] == 'clean':
        do_clean(conn, settings['schema'])
    elif settings['operation'] == 'migration':
        do_migration(conn, settings['schema'], settings['baseline'])

if __name__=='__main__':
    main()
